#ifndef PARSER_HPP
#define PARSER_HPP

#include "lexer.hpp"

#include <iostream>
#include <string>
using namespace std;

class Node;
class VarNode;
class CmpNode;
class TypeNode;
class ListNode;
class StmtsNode;
class ImportNode;
class SymbolTable;
class TypeTable;

class Parser {
private:
    Lexer* _lex;
    Token* _cur_token;

    SymbolTable*    _sym_table;
    TypeTable*      _type_table;
    
public:
    Parser(Lexer* lex, SymbolTable* symtable);
    ~Parser();

    Node* parse();

    void consume();
    Token* get_token();
    void match(TokenType tt);
    
    Node* stmts();
    Node* stmt();
    Node* print_stmt();
    Node* return_stmt();
    Node* if_stmt();
    Node* code_block();
    Node* while_stmt();
    Node* var_def_stmt();
    Node* val_def_stmt();
    Node* assign_stmt(Node* left);
    Node* new_clause();
    Node* import_stmt();
    Node* from_import_stmt();
    Node* trait_def();
    Node* trait_methods_list();
    Node* trait_method_def();
    
    Node* param_def();
    ListNode* parameters();
    Node* func_def();
    Node* record_def();
    Node* field_list();
    Node* field_def();
    Node* implement_stmt();
    Node* methods_list();
    Node* method_def();

    Node* lambda_def();
    Node* func_call();

    CmpNode* cmp_op();
    Node* expression();
    Node* or_test();
    Node* and_test();
    Node* not_test();
    Node* comparison();
    Node* or_expr();
    Node* xor_expr();
    Node* and_expr();
    Node* shift_expr();
    Node* arith_expr();
    Node* term();
    Node* factor();
    Node* atom();
    bool is_trailer_char(TokenType tt);
    /**
     * Process trailing expressions like function calls f() and subscripts f[].
     * For f[a], we use lightweight type lookup to select the correct grammar:
     * - If f is a generic function: parse as type_arguments() -> f[T1, T2, ...]
     * - Otherwise: parse as expression() -> f[expr]
     * This ensures correct parsing while maintaining clean architecture.
     */
    Node* trails(Node* left);
    int stoi(Token* data);
    double stod(Token* data);

    /* types */
    Node* typedef_stmt();
    Node* type_atom();
    Node* type_stmt();

    Node* type_param();
    ListNode* type_arguments();
    ListNode* type_parameters();
    ListNode* expr_list();

    bool is_generic_type(VarNode* var_node);

    Node* do_import(ImportNode* n);
    Node* operator_def(string name);
};

void eval(const char* s);

#endif

